#include "SharedFileName.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <omp.h>
#include <map>
using namespace std;

// return number of edges
int LoadGraph(vector<vector<int> >& graph_adj_list,string in_dir){
	string adj_file(in_dir);
	adj_file.append(sample_adjlist_file);
	ifstream graphAdj(adj_file.c_str());
	cout << "adj_file : " << adj_file << endl;
	if(!graphAdj.is_open()){
		cout << "canont open graph adjlist" << endl;
		exit(1);
	}

	int num_edges = 0;

	string line;
	getline(graphAdj,line); // skip the first line
	int number_nodes = atoi(line.c_str());
	graph_adj_list.resize(number_nodes);

	int node_idx = 0;
	while(getline(graphAdj,line)){
		stringstream ss(line);
		int num_neighbours;
		ss >> num_neighbours;
		for(int i = 0; i < num_neighbours; i++){
			int neighbour;
			if(!(ss >> neighbour)){
				cout << "adjcency list problem" << endl;
				exit(1);
			}
			num_edges++;
			graph_adj_list[node_idx].push_back(neighbour);
		}
		node_idx++;
	}
	graphAdj.close();
	cout << "finish reading adj list with number of edges: " << num_edges << endl;

	for(int i = 0; i < graph_adj_list.size(); i++){
		sort(graph_adj_list[i].begin(),graph_adj_list[i].end());
	}

	return num_edges/2;
}

void ComputeSocialScore(vector<vector<int> >& graph_adj_list,int num_edges, string out_dir){
	vector<map<int,int > > weighted_graph(graph_adj_list.size());
	int count = 0;
	#pragma omp parallel num_threads(20)
	{

	#pragma omp for
	for(int i = 0; i < graph_adj_list.size(); i++){
		vector<int>& a_set = graph_adj_list[i];
		for(int j = 0; j < a_set.size(); j++){
			if(a_set[j] < i) continue;
			vector<int>& b_set = graph_adj_list[a_set[j]];
			

			// compute the jarccard distance
			int common_elems = 0;
			vector<int> common_set(a_set.size()+b_set.size());
			vector<int>::iterator it;
			it = set_intersection(a_set.begin(),a_set.end(),b_set.begin(),b_set.end(),common_set.begin());
			common_elems = it - common_set.begin();

			double jar_dist = 1.0 - (double)common_elems/(a_set.size() + b_set.size() - common_elems);
			jar_dist *= 100;
			int dist = static_cast<int>(jar_dist);

			/*if(a_set.size() == b_set.size() && common_elems == a_set.size()){
				cout << "node " << i << " and node " << a_set[j] << " has problem" << endl;
				cout << "they have set size: " << a_set.size() << " and " << b_set.size() << endl;
				cout << "common size: " << common_elems << endl;
				cout << "jarcarrd distance equal to 0, problem!" << endl;
				exit(1);
			}*/

			weighted_graph[i].insert(make_pair(a_set[j],dist));
		}

		#pragma omp critical
		{
			count++;	
			if(count % 100000 == 0) cout << "processed " << count << " elems" << endl;
		}
	}

	}
	cout << "finished processing computing distance" << endl;
	
	
	ofstream weighedGraphFile(out_dir.append(sample_weighted_adjlist_file).c_str(),ios::out);

	weighedGraphFile << graph_adj_list.size() << " " << num_edges << " 001" << endl;
	for(int i = 0; i < graph_adj_list.size(); i++){
		vector<int>& neighbours = graph_adj_list[i];
		for(int j = 0; j < neighbours.size(); j++){
			int weight = -1;
			if(neighbours[j] < i){
				weight = weighted_graph[neighbours[j]].find(i)->second;
			}else{
				weight = weighted_graph[i].find(neighbours[j])->second;
			}
			weight = 100 - weight; // spcial arrangement
			if(weight == 0) weight = 1;
			// output should increment 1 for all nodes for metis's format
			weighedGraphFile << (neighbours[j]+1) << " " << weight << " ";
		}
		weighedGraphFile << endl;
		if(i % 10000 == 0) cout << "finish writing " << i / 10000 << " x10k nodes" << endl;
	}

	cout << "finish output the adj matrix" << endl;

	weighedGraphFile.close();

}


int main(int argc, char *argv[]){

	if(argc != 2){
		cout << "wrong arguments for " << argv[0] << endl;
		cout << "1. input directory" << endl;
		exit(1);
	}

	string in_dir(argv[1]);

	vector<vector<int> > graph_adj_list;
	int num_edges = LoadGraph(graph_adj_list,in_dir);
	ComputeSocialScore(graph_adj_list,num_edges,in_dir);

	return 1;
}